import Controller from './Controller';

export default class ObjectPicController extends Controller {

	constructor( parent, object, property,config ) {
		if(config == null) config = {} ;
		super( parent, object, property, 'boolean', 'label',config );

		this.isObjectPicController = true ;

		this.$span = document.createElement("span") ;

		this.$input = document.createElement( 'input' );
		this.$input.setAttribute( 'type', 'checkbox' );
		this.$input.setAttribute( 'aria-labelledby', this.$name.id );
		this.$span.appendChild( this.$input );

		this.$button = document.createElement( 'button' );
		this.$button.classList.add( 'objectPicButton' );
		let imageUrl = this.getValue().imageUrl ;
		// if(imageUrl == null || imageUrl == "" || imageUrl.endsWith(".hdr"))
		// 	imageUrl = this.defaultImage() ;
		this.$image = document.createElement("img") ;
		this.$image.src = this.checkAndSetImage(imageUrl) ;
		this.$image.classList.add("picImgTag") ;
		this.$button.appendChild( this.$image );

		this.$span.appendChild( this.$button );

		//按钮
		let btnTxt = this.getValue().btnTxt ;
		if(btnTxt != null && btnTxt != "" && this.getValue().btnClick != null){
			this.$button1 = document.createElement( 'button' );
			this.$button1.innerHTML = btnTxt;
			this.$button1.classList.add("objectPicBtn1") ;
			this.$span.appendChild( this.$button1 );

			this.$button1.addEventListener( 'click', e => {
				e.preventDefault();
				if(this.getValue().enable == false)
					return ;
				this.getValue().btnClick.call( this.getValue(),this,this.object);
				// this._callOnChange();
			} );
		}

		this.__initNumberInput() ;

		this.$widget.appendChild( this.$span );

		this.$input.addEventListener( 'change', () => {
			this.setValue( this.$input.checked );
			this._callOnFinishChange();
		} );

		this.$button.addEventListener( 'click', e => {
			e.preventDefault();
			if(this.getValue().enable == false)
				return ;
			this.getValue().click.call( this.getValue(),this,this.object);
			this._callOnChange();
		} );

		this.$disable = this.$input;

		this._appendEndDomHtml(config.endHtml) ;

		this.updateDisplay();

	}

	updateDisplay() {
		this.$input.checked = this.getValue().enable;
		let imageUrl = this.getValue().imageUrl ;
		this.$image.src = this.checkAndSetImage(imageUrl) ;
		return this;
	}

	setValue( value ) {
		this.object[ this.property ].enable = value;
		this._callOnChange();
		this.updateDisplay();
		return this;
	}

	checkAndSetImage(imageUrl){
		if(this.$button != null){
			this.$button.title = "" ;
			if(imageUrl != null && imageUrl != ""){
				//获取文件名
				let index = imageUrl.lastIndexOf("/") ;
				if(index != -1){
					this.$button.title = imageUrl.substr(index+1) ;
				}
			}
		}

		if(imageUrl == null || imageUrl == "" || imageUrl.endsWith(".hdr"))
			imageUrl = "" ;

		return imageUrl ;
	}

	step( step, explicit = true ) {
		this._step = step;
		this._stepExplicit = explicit;
		return this;
	}
	min( min ) {
		this._min = min;
		return this;
	}

	max( max ) {
		this._max = max;
		return this;
	}

	__initNumberInput() {
		//数字及数字改变函数不为空
		let numberObj = this.getValue().numberObj;
		if (numberObj == null)
			return;
		if(numberObj.enable === false) return ;

		this.numberOnChange = numberObj.onChange ;

		this._stepExplicit = true ;
		this._min = numberObj.min ;
		if(this._min == null) this._min = -Infinity ;
		this._max = numberObj.max ;
		if(this._max == null) this._max = Infinity ;
		this._step = numberObj._step ;
		if(this._step == null) this._step = 1 ;
		this.$input2 = document.createElement( 'input' );
		this.$input2.setAttribute( 'type', 'text' );
		this.$input2.setAttribute( 'style', "width:55px;margin-left:10px;" );
		this.$input2.value = this._clamp(numberObj.value) ;
		if(numberObj.tooltip != null)
			this.$input2.setAttribute("title",numberObj.tooltip) ;
		this.$span.appendChild( this.$input2 );


		const onInput = () => {

			let value = parseFloat( this.$input2.value );

			if ( isNaN( value ) ) return;

			if ( this._stepExplicit ) {
				value = this._snap( value );
			}

			value = this._clamp( value ) ;
			this._setNumberValue(value) ;
		};

		// Keys & mouse wheel
		// ---------------------------------------------------------------------

		const increment = delta => {

			const value = parseFloat( this.$input2.value );

			if ( isNaN( value ) ) return;
			this._snapClampSetValue( value + delta );

			// Force the input to updateDisplay when it's focused
			// this.$input2.value = this.getValue();

		};

		const onKeyDown = e => {
			this.setEventType(e.key) ;
			// Using `e.key` instead of `e.code` also catches NumpadEnter
			if ( e.key === 'Enter' ) {
				this.$input2.blur();
			}
			if ( e.code === 'ArrowUp' ) {
				e.preventDefault();
				increment( this._step * this._arrowKeyMultiplier( e ) );
			}
			if ( e.code === 'ArrowDown' ) {
				e.preventDefault();
				increment( this._step * this._arrowKeyMultiplier( e ) * -1 );
			}
		};

		const onWheel = e => {
			this.setEventType("Wheel") ;
			if ( this._inputFocused ) {
				e.preventDefault();
				increment( this._step * this._normalizeMouseWheel( e ) );
			}
		};

		// Vertical drag
		// ---------------------------------------------------------------------

		let testingForVerticalDrag = false,
			initClientX,
			initClientY,
			prevClientY,
			initValue,
			dragDelta;

		// Once the mouse is dragged more than DRAG_THRESH px on any axis, we decide
		// on the user's intent: horizontal means highlight, vertical means drag.
		const DRAG_THRESH = 5;

		const onMouseDown = e => {
			this.setEventType("MouseDown") ;

			initClientX = e.clientX;
			initClientY = prevClientY = e.clientY;
			testingForVerticalDrag = true;

			initValue = parseFloat( this.$input2.value ) ;
			dragDelta = 0;

			window.addEventListener( 'mousemove', onMouseMove );
			window.addEventListener( 'mouseup', onMouseUp );

		};

		const onMouseMove = e => {
			this.setEventType("MouseMove") ;

			if ( testingForVerticalDrag ) {

				const dx = e.clientX - initClientX;
				const dy = e.clientY - initClientY;

				if ( Math.abs( dy ) > DRAG_THRESH ) {

					e.preventDefault();
					this.$input2.blur();
					testingForVerticalDrag = false;
					this._setDraggingStyle( true, 'vertical' );

				} else if ( Math.abs( dx ) > DRAG_THRESH ) {

					onMouseUp();

				}

			}

			// This isn't an else so that the first move counts towards dragDelta
			if ( !testingForVerticalDrag ) {

				const dy = e.clientY - prevClientY;

				dragDelta -= dy * this._step * this._arrowKeyMultiplier( e );

				// Clamp dragDelta so we don't have 'dead space' after dragging past bounds.
				// We're okay with the fact that bounds can be undefined here.
				if ( initValue + dragDelta > this._max ) {
					dragDelta = this._max - initValue;
				} else if ( initValue + dragDelta < this._min ) {
					dragDelta = this._min - initValue;
				}

				this._snapClampSetValue( initValue + dragDelta );

			}

			prevClientY = e.clientY;

		};

		const onMouseUp = () => {
			this.setEventType("MouseUp") ;
			this._setDraggingStyle( false, 'vertical' );
			// this._callOnFinishChange();
			window.removeEventListener( 'mousemove', onMouseMove );
			window.removeEventListener( 'mouseup', onMouseUp );
		};

		// Focus state & onFinishChange
		// ---------------------------------------------------------------------

		const onFocus = () => {
			this._inputFocused = true;
		};

		const onBlur = () => {
			this._inputFocused = false;
			// this.updateDisplay();
			// this._callOnFinishChange();
		};

		this.$input2.addEventListener( 'input', onInput );
		this.$input2.addEventListener( 'keydown', onKeyDown );
		this.$input2.addEventListener( 'wheel', onWheel, { passive: false } );
		this.$input2.addEventListener( 'mousedown', onMouseDown );
		this.$input2.addEventListener( 'focus', onFocus );
		this.$input2.addEventListener( 'blur', onBlur );

	}

	_clamp( value ) {
		// either condition is false if min or max is undefined
		if (this._min != null && value < this._min ) value = this._min;
		if (this._max != null && value > this._max ) value = this._max;
		return value;
	}

	_snap( value ) {
		const r = Math.round( value / this._step ) * this._step;
		return parseFloat( r.toPrecision( 15 ) );
	}
	_snapClampSetValue( value ) {
		// this.$input2.value = this._clamp( this._snap( value ) );
		this._setNumberValue(this._clamp( this._snap( value ) )) ;
	}
	_arrowKeyMultiplier( e ) {
		let mult = this._stepExplicit ? 1 : 10;
		if ( e.shiftKey ) {
			mult *= 10;
		} else if ( e.altKey ) {
			mult /= 10;
		}
		return mult;
	}
	setEventType(eventType){
		this.eventType = eventType ;
	}
	_normalizeMouseWheel( e ) {
		let { deltaX, deltaY } = e;
		if ( Math.floor( e.deltaY ) !== e.deltaY && e.wheelDelta ) {
			deltaX = 0;
			deltaY = -e.wheelDelta / 120;
			deltaY *= this._stepExplicit ? 1 : 10;
		}
		const wheel = deltaX + -deltaY;
		return wheel;
	}
	_setDraggingStyle( active, axis = 'horizontal' ) {
		document.body.classList.toggle( 'lil-gui-dragging', active );
		document.body.classList.toggle( `lil-gui-${axis}`, active );
	}
	_setNumberValue(value){
		this.$input2.value = value ;
		this.getValue().numberObj.value = value;
		if(this.numberOnChange == null) return ;
		this.numberOnChange.call( null, value,this.getValue(),this.$input2,this);
		return this;
	}
}
